<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="3c4e0a25" id="3c4e0a25"><span data-lake-id="ua5e14c43" id="ua5e14c43">典型回答</span></h1>
  <p data-lake-id="ufd14ea1c" id="ufd14ea1c"><br></p>
  <p data-lake-id="u87440d7e" id="u87440d7e"><span data-lake-id="uc323efed" id="uc323efed">通常我们会使用ELK收集并查看日志，查询日志通过ES语法实现。但是有的时候，还是免不了要去服务器上查看日志。在Linux下，常用的有2种方式查看日志：</span></p>
  <p data-lake-id="u60e44ba8" id="u60e44ba8"><br></p>
  <ol list="u0130d7da">
   <li fid="u0d1db12a" data-lake-id="u0c99b2b9" id="u0c99b2b9"><span data-lake-id="ud44319c3" id="ud44319c3">使用</span><code data-lake-id="ub2d9ba62" id="ub2d9ba62"><span data-lake-id="u6edf03a2" id="u6edf03a2">tail -f 日志文件名</span></code><span data-lake-id="u147e7730" id="u147e7730">滚动输出日志文件。适用于实时观察日志的场景。</span></li>
  </ol>
  <p data-lake-id="u7357dcdb" id="u7357dcdb"><br></p>
  <ol list="u0130d7da" start="2">
   <li fid="u0d1db12a" data-lake-id="uc187e54f" id="uc187e54f"><span data-lake-id="u769bba24" id="u769bba24">使用</span><code data-lake-id="u8effaad3" id="u8effaad3"><span data-lake-id="u4306f5d5" id="u4306f5d5">less 日志文件名</span></code><span data-lake-id="u1cbce1b3" id="u1cbce1b3">查看整个日志文件，并按</span><code data-lake-id="u314bd8ef" id="u314bd8ef"><span data-lake-id="u77494b04" id="u77494b04">空格</span></code><span data-lake-id="u8e6e53ea" id="u8e6e53ea">向下翻页，按</span><code data-lake-id="u74ab9be5" id="u74ab9be5"><span data-lake-id="u80a554f1" id="u80a554f1">ctrl+b</span></code><span data-lake-id="ubdeca3a1" id="ubdeca3a1">向上翻页，按</span><code data-lake-id="u3f8c080a" id="u3f8c080a"><span data-lake-id="u1e2cfdc7" id="u1e2cfdc7">/</span></code><span data-lake-id="ufc985147" id="ufc985147">并输入</span><code data-lake-id="ue8df3f7a" id="ue8df3f7a"><span data-lake-id="ua0db4b29" id="ua0db4b29">关键词</span></code><span data-lake-id="u0ae7fedd" id="u0ae7fedd">向下搜索，按</span><code data-lake-id="u113c6fa1" id="u113c6fa1"><span data-lake-id="u3a3ff069" id="u3a3ff069">?</span></code><span data-lake-id="u05079df2" id="u05079df2">并输入</span><code data-lake-id="uba67b94e" id="uba67b94e"><span data-lake-id="u4e46c54a" id="u4e46c54a">关键词</span></code><span data-lake-id="u8263e3ec" id="u8263e3ec">向上搜索，按</span><code data-lake-id="u4c7c865d" id="u4c7c865d"><span data-lake-id="u260680bb" id="u260680bb">q</span></code><span data-lake-id="u10daf352" id="u10daf352">退出。适用于通篇查看日志的场景。</span></li>
  </ol>
  <p data-lake-id="ue914ef8c" id="ue914ef8c"><br></p>
  <p data-lake-id="u6fa58dbf" id="u6fa58dbf"><span data-lake-id="u0ce4c022" id="u0ce4c022">除了日志的查看，有的时候我们还需要做一些分析，比如统计QPS，按照耗时排序等。</span></p>
  <p data-lake-id="u38226745" id="u38226745"><span data-lake-id="u56ce42af" id="u56ce42af">​</span><br></p>
  <p data-lake-id="u22387433" id="u22387433"><span data-lake-id="ua4bd8681" id="ua4bd8681">在不用ELK或者其他第三方日志平台的情况下，我们使用Linux命令是完全可以做到的。但只是</span><code data-lake-id="uaf2c7c2e" id="uaf2c7c2e"><span data-lake-id="u9a40c8a1" id="u9a40c8a1">tail</span></code><span data-lake-id="u883106be" id="u883106be">和</span><code data-lake-id="u4114b379" id="u4114b379"><span data-lake-id="uf91a04a8" id="uf91a04a8">less</span></code><span data-lake-id="u894d6986" id="u894d6986">命令完成不了上述需求，此时我们需要使用</span><code data-lake-id="u120fc7cd" id="u120fc7cd"><span data-lake-id="u02e8bd50" id="u02e8bd50">grep</span></code><span data-lake-id="u6a1cba27" id="u6a1cba27">、</span><code data-lake-id="u55a7a6c3" id="u55a7a6c3"><span data-lake-id="ue308501d" id="ue308501d">awk</span></code><span data-lake-id="u6362ed6f" id="u6362ed6f">、</span><code data-lake-id="ub6fc67f6" id="ub6fc67f6"><span data-lake-id="u72ebc43c" id="u72ebc43c">sort</span></code><span data-lake-id="u9cbaa322" id="u9cbaa322">、</span><code data-lake-id="u0e95fa2e" id="u0e95fa2e"><span data-lake-id="u7274de51" id="u7274de51">uniq</span></code><span data-lake-id="uf877e58f" id="uf877e58f">、</span><code data-lake-id="ue1350b51" id="ue1350b51"><span data-lake-id="ubd65a912" id="ubd65a912">wc</span></code><span data-lake-id="ubb952395" id="ubb952395">等命令进行组合才能达到目的。这些命令组合不只用于分析日志，也可以用到其他分析场景（如统计磁盘、CPU使用情况）。</span></p>
  <p data-lake-id="u0b7a4b48" id="u0b7a4b48"><span data-lake-id="u851852a1" id="u851852a1">​</span><br></p>
  <p data-lake-id="ub966e4ea" id="ub966e4ea"><span data-lake-id="u7e42cadd" id="u7e42cadd">如以下需要做数据分析场景：</span></p>
  <p data-lake-id="u36433a73" id="u36433a73"><br></p>
  <ol list="u3319486e">
   <li fid="u28c833a2" data-lake-id="ue67eaaa7" id="ue67eaaa7"><span data-lake-id="u2684a664" id="u2684a664">统计httpClient日志文件http_access.log中，某个接口的一天总请求量是多少。</span></li>
   <li fid="u28c833a2" data-lake-id="u1495971b" id="u1495971b"><span data-lake-id="u58c44365" id="u58c44365">统计客户端访问日志access.log中，返回http状态码为500的接口平均QPS是多少。</span></li>
   <li fid="u28c833a2" data-lake-id="u84fc2760" id="u84fc2760"><span data-lake-id="ue2b19800" id="ue2b19800">统计access.log中，某个接口耗时大于500ms，并按照耗时排序。</span></li>
  </ol>
  <p data-lake-id="u35e7ab20" id="u35e7ab20"><span data-lake-id="u575cc26e" id="u575cc26e">​</span><br></p>
  <p data-lake-id="uffe323fc" id="uffe323fc"><span data-lake-id="ube2e1f3c" id="ube2e1f3c">下面介绍几个常用的命令及使用方式。</span></p>
  <h1 data-lake-id="dc9cb89a" id="dc9cb89a"><span data-lake-id="u770f832f" id="u770f832f">扩展知识</span></h1>
  <p data-lake-id="u6e3e52e9" id="u6e3e52e9"><br></p>
  <ul list="ud344835d">
   <li fid="u7a76827d" data-lake-id="u507d89b4" id="u507d89b4"><code data-lake-id="u241f1c86" id="u241f1c86"><span data-lake-id="u608a4c03" id="u608a4c03">grep</span></code><span data-lake-id="u22af2cf2" id="u22af2cf2">命令：用于查找关键词，常用玩法：</span></li>
  </ul>
  <p data-lake-id="ua3d07e4e" id="ua3d07e4e"><br></p>
  <pre lang="java"><code>
grep '/xxx.json' http_access.log  #查找http_access.log中包含xxx.json的接口
grep -C10 'NullPointException' warng.log  #查找warn.log中包含NullPointException的前后10行
grep -i "asc" info.log  #查找info.log中包含asc的行，忽略字符大小写
grep "ASC|asc" info.log  #使用正则表达式匹配asc或者ASC
</code></pre>
  <p data-lake-id="u0f4e9acc" id="u0f4e9acc"><br></p>
  <ul list="uec2cef8e">
   <li fid="ud5559715" data-lake-id="ue66daac6" id="ue66daac6"><code data-lake-id="ub45ac3c1" id="ub45ac3c1"><span data-lake-id="u45db8875" id="u45db8875">wc</span></code><span data-lake-id="u9bd3390a" id="u9bd3390a">命令：用于字词统计，常用玩法：</span></li>
  </ul>
  <p data-lake-id="uc34ce9ce" id="uc34ce9ce"><br></p>
  <pre lang="java"><code>
grep '/xxx.json' http_access.log | wc -l  #查找http_access.log中接口名包含xxx.json的请求总共有多少条，wc -l起到统计行数的作用
</code></pre>
  <p data-lake-id="u4076dff6" id="u4076dff6"><br></p>
  <ul list="u40ef56fc">
   <li fid="u5728857e" data-lake-id="u115431eb" id="u115431eb"><code data-lake-id="ub389d57c" id="ub389d57c"><span data-lake-id="uc89ee9ef" id="uc89ee9ef">sort</span></code><span data-lake-id="u133be5dd" id="u133be5dd">命令：用于排序，常用玩法：</span></li>
  </ul>
  <p data-lake-id="ud4497d63" id="ud4497d63"><br></p>
  <pre lang="java"><code>
# 若test.txt文件内容如下
aa 1
cc 2
bb 0

sort test.txt #按字符顺序排序，结果为
aa 1
bb 0
cc 2

sort -r test.txt #按字符顺序降序，结果为
cc 2
bb 0
aa 1

sort -n -k2 test.txt #按第二列排序（-k2的作用） -n按数字顺序排序，结果为
bb 0
aa 1
cc 2

# 若test.txt文件内容如下
 aa,1
 cc,2
 bb,0

sort -n -k2 -t ',' test.txt #以,作为分隔符（-t ','的作用）并按第二列排序（-k2的作用） -n按数字顺序排序 ，结果为
bb,0
aa,1
cc,2
</code></pre>
  <p data-lake-id="u7f946cf7" id="u7f946cf7"><br></p>
  <ul list="u444a6089">
   <li fid="u7c7ca631" data-lake-id="u4aad7906" id="u4aad7906"><code data-lake-id="u6f5e27f8" id="u6f5e27f8"><span data-lake-id="uc39c2d9a" id="uc39c2d9a">uniq</span></code><span data-lake-id="u98557e86" id="u98557e86">命令：用于去重，常用玩法：</span></li>
  </ul>
  <p data-lake-id="u4e2e6c22" id="u4e2e6c22"><br></p>
  <pre lang="java"><code>
grep 'Exception' warn.log| uniq -c #统计warn.log中的异常，去重并显示每种异常有多少个，uniq -c起到去重并统计相同字符出现的次数

#uniq经常与sort一起使用，玩法：
# 若test.txt文件如下：
aa,1
aa,1
cc,2
bb,0
bb,0

sort -k2 -t ',' -n test.txt|uniq -D #按第二列数字排序，并且只输出重复的行（uniq -D的作用），结果为
bb,0
bb,0
aa,1
aa,1

sort -k2 -t ',' -n test.txt|uniq -D #按第二列数字排序，并且只输出重复的行，只输出一次（uniq -d的作用），结果为
bb,0
aa,1

sort -k2 -t ',' -n test.txt|uniq #按第二列数字排序，并且去重，结果为
bb,0
aa,1
cc,2

sort -u -k2 -t ',' -n test.txt #输出的结果与上面|uniq相同（sort -u的作用）

sort -k2 -t ',' -n test.txt|uniq -u #按第二列数字排序，只输出不重复的行，结果为（uniq -u的作用）
cc,2

sort -k2 -t ',' -n test.txt|uniq -c #按第二列数字排序，统计重复行数（uniq -c的作用），结果为（第一列为相同行出现几次）
2 bb,0
2 aa,1
1 cc,2
</code></pre>
  <p data-lake-id="u8332d11e" id="u8332d11e"><br></p>
  <ul list="uefd5de95">
   <li fid="u5c4acd98" data-lake-id="uf006f148" id="uf006f148"><code data-lake-id="u450ce560" id="u450ce560"><span data-lake-id="u3d2e7117" id="u3d2e7117">awk</span></code><span data-lake-id="u564a88b2" id="u564a88b2">命令：用于处理文本，awk是个非常强大的命令，能够处理输入的每一行字符串，玩法众多（够写一本书了），这里只介绍常用的：</span></li>
  </ul>
  <p data-lake-id="u3133531a" id="u3133531a"><br></p>
  <pre lang="java"><code>
# 若test.txt文件如下：
aa 1
bb 1
cc 2

awk '{print $1}' test.txt #按空白字（默认）符拆分每一行为多列，这里$1代表输出第1列，$0代表一整行所有列，$NF代表最后一列，以上命令结果为：
aa
bb
cc

awk -F ',' #-F参数可以修改列的分隔符，这里改为按逗号分割

awk 'BEGIN{print "开始处理每行"}{print $1}END{print "结束处理"}' test.txt #特殊模式，BEGIN在处理每一行之前执行，END在处理每一行完成后执行，以上命令结果为：
开始处理每行
aa
bb
cc
结束处理
</code></pre>
  <p data-lake-id="u5e9d153d" id="u5e9d153d"><br></p>
  <p data-lake-id="ucec39d50" id="ucec39d50"><span data-lake-id="ufc92e762" id="ufc92e762">我们利用以上的命令，来回答扩展阅读开头的三个问题：</span></p>
  <p data-lake-id="u5a6f0885" id="u5a6f0885"><br></p>
  <blockquote data-lake-id="u1d8ca9f1" id="u1d8ca9f1">
   <p data-lake-id="u32b7db2c" id="u32b7db2c"><span data-lake-id="u21ccfb35" id="u21ccfb35">说明：access.log和http_access.log遵循如下格式：<br></span><code data-lake-id="u6c631810" id="u6c631810"><span data-lake-id="uc052f05f" id="uc052f05f">[日志级别] 请求时间年月日 请求时间时分秒 [请求线程名称] - 请求方法 响应状态码 耗时 请求URL requestID</span></code><span data-lake-id="u8dfea92b" id="u8dfea92b"><br>
     每组信息中间用空格分开。<br>
     如：<br></span><code data-lake-id="u74c69eef" id="u74c69eef"><span data-lake-id="u9fddabe9" id="u9fddabe9">[INFO] 20231023 13:00:27.049 [abcService-exec-1] - 2 GET 200 600 http://xxx.com/abc/xxx.json d7af416bc1390dc4d9124f147bab4e53 [INFO] 20231023 13:00:27.049 [abcService-exec-1] - 2 GET 500 800 http://xxx.com/abc/yyy.json e7af416bc1390dc4d9124f147bab4e52 [INFO] 20231023 13:00:27.049 [abcService-exec-1] - 2 GET 200 810 http://xxx.com/abc/yyy.json e7af416bc1390dc4d9124f147bab4e52 [INFO] 20231023 13:00:27.050 [abcService-exec-1] - 2 GET 500 210 http://xxx.com/abc/yyy.json e7af416bc1390dc4d9124f147bab4e52 [INFO] 20231023 13:00:27.049 [abcService-exec-1] - 2 GET 200 550 http://xxx.com/abc/aaa.json f7af416bc1390dc4d9124f147bab4e51 [INFO] 20231023 13:00:27.049 [abcService-exec-1] - 2 GET 200 50 http://xxx.com/abc/zzz.json f7af416bc1390dc4d9124f147bab4e51</span></code></p>
  </blockquote>
  <p data-lake-id="ub84215a7" id="ub84215a7"><br></p>
  <p data-lake-id="u7c2437a3" id="u7c2437a3"><strong><span data-lake-id="u9138f1de" id="u9138f1de">统计httpClient日志文件http_access.log中，某个接口的一天总请求量是多少。</span></strong></p>
  <p data-lake-id="u7eeb7dfe" id="u7eeb7dfe"><br></p>
  <pre lang="java"><code>
grep "/yyy.json" http_access.log|wc -l
# 结果为：
3

#解释：
1. 通过grep 查找`/yyy.json`的接口请求
2. 通过wc统计总行数，得出总量
</code></pre>
  <p data-lake-id="ud31b2a06" id="ud31b2a06"><br></p>
  <p data-lake-id="ue06e997a" id="ue06e997a"><strong><span data-lake-id="ud1f42d98" id="ud1f42d98">统计客户端访问日志access.log中，返回http状态码为500的接口平均QPS是多少。</span></strong></p>
  <p data-lake-id="u6059c008" id="u6059c008"><br></p>
  <pre lang="java"><code>
grep "/yyy.json" access.log|grep ' 500 ' |awk '{print substr($0,10,15)}'  | uniq -c  | awk '{sum += $1} END {print sum / NR}'

#结果为：
2

#解释：
1. grep "/yyy.json" access.log：首先，使用 grep 命令从access.log 文件中筛选出包含 "/yyy.json" 的行。
2. grep ' 500 '：接下来，使用第二个 grep 命令从前一步的结果中筛选出包含 "500" 的行。这个命令用于查找 HTTP 状态码为 500 的行，表示服务器内部错误。
3. awk '{print substr($0,10,15)}'：使用 awk 命令提取每一行的第 10 到第 25 个字符（共 15 个字符）并打印。这个命令用于提取出包含日期和时间的部分，例如 "20231023 13:00:27"。
4. uniq -c：接着，使用 uniq 命令计算并显示每个唯一的时间戳出现的次数。-c 选项用于在每行前面显示出现的次数。
5. awk '{sum += $1} END {print sum / NR}'：最后，使用第二个 awk 命令计算出现次数的平均值。它将每一行的第一个字段（出现次数）累加到 sum 变量中，然后在处理完所有行后，通过除以行数 NR 得到平均值，并将结果打印出来。
</code></pre>
  <p data-lake-id="u59802c5e" id="u59802c5e"><br></p>
  <p data-lake-id="ufd3b3d0d" id="ufd3b3d0d"><strong><span data-lake-id="u50ed8912" id="u50ed8912">统计access.log中，某个接口耗时大于500ms，并按照耗时排序。</span></strong></p>
  <p data-lake-id="u45d13ed4" id="u45d13ed4"><br></p>
  <pre lang="java"><code>
awk '$9&gt;500{print $0}' access.log | sort -k9 -n -r |awk '!arr[$10]++ {print $9"ms",$10}' 

#结果为：
810ms http://xxx.com/abc/yyy.json
600ms http://xxx.com/abc/xxx.json
550ms http://xxx.com/abc/aaa.json

#解释：
1. awk 默认将access.log每一行按空格拆分，第9列为响应耗时，$9&gt;500为响应耗时超过500毫秒，{print $0}打印符合条件的整行
2. sort同样默认按空格拆分每一行，对第9列按数字倒序排列
3. 第10列为请求地址，arr[$10] 创建了一个关联数组，用于存储第10列的值。!arr[$10]++ 通过判断数组中的值是否已存在，实现了按第10列去重的功能。如果某个请求地址是第一次出现，则打印该行的第99列和第10列。其中第9列拼个ms打印。
</code></pre>
  <p data-lake-id="u27143c0f" id="u27143c0f"><br></p>
  <p data-lake-id="u7b7309be" id="u7b7309be"><span data-lake-id="u3f177c5b" id="u3f177c5b">awk命令比较复杂，有一本书叫做《sed与awk》，详细讲解俩个命令如何处理文本的。也可以多敲多练，遇到不会的拿样本数据问问chatgpt，会给出正确的命令和详细的解释。</span></p>
 </body>
</html>